﻿using System;
using System.Collections.Generic;
using System.Linq;
using StockEngine.Properties;
using StockModel;
using Utils;
using Utils.Extensions;

namespace StockEngine.RealTime
{
	internal class AcquisitionScheduler : TimeredEngineBase
	{
		#region Attributes

		private readonly ExchangeMarketDataSource  _marketDataSource;
		private TimeSpan _startTime;
		private TimeSpan _stopTime;
		
		private bool _initialized;
		private bool _startRequested;

        private List<DateTime> _allMarketsHolidays;

		private DateTime? _lastCheck;

		#endregion

		#region Properties

		public DateTime? LastCheck
		{
			get
			{
				return _lastCheck;
			}
			private set
			{
				if (value != _lastCheck)
				{
					_lastCheck = value;
					base.OnPropertyChanged("LastCheck");
				}
			}
		}

		#endregion

		#region Events

		public event EventHandler StartRequired;
		public event EventHandler<StopRequiredEventArgs> StopRequired;

		#endregion

		#region Public Methods

		public AcquisitionScheduler(ExchangeMarketDataSource marketDataSource) : base(Settings.Default.SchedulerInterval, "Acquisition Scheduler")
		{
			_marketDataSource = marketDataSource;
		}

		public void Initialize()
		{
			IEnumerable<ExchangeMarket> markets = _marketDataSource.Load();

			// ReSharper disable PossibleMultipleEnumeration
			_startTime = markets.Min(m => m.PreOpen);
			_stopTime = markets.Max(m => m.Closed).Add(Properties.Settings.Default.IntervalForClosedQuote);

            this.GetAllMarketsHolidays(markets);
			// ReSharper restore PossibleMultipleEnumeration

			_initialized = true;
		}

		public override void Stop()
		{
			base.Stop();
			this.OnStopRequired(false);
		}

		protected override void MainMethod()
		{
			if (!_initialized)
			{
				throw new InvalidOperationException("The Acquisition engine scheduler must be initialized before using it");
			}

            DateTime today = DateTime.Now.Date;
            TimeSpan currentTime = DateTime.Now.TimeOfDay;

            if (!_startRequested && !today.IsWeekend() && currentTime > _startTime && currentTime < _stopTime && !_allMarketsHolidays.Contains(today))
			{
				this.OnStartRequired();
			}
            else if (_startRequested && (currentTime < _startTime || currentTime > _stopTime || today.IsWeekend() || _allMarketsHolidays.Contains(today)))
			{
				this.OnStopRequired(true);
			}

			this.LastCheck = DateTime.Now;
		}

		protected virtual void OnStartRequired()
		{
			_startRequested = true;

			if (this.StartRequired != null)
			{
				this.StartRequired(this, EventArgs.Empty);
			}
		}

		protected virtual void OnStopRequired(bool waitEngineReady)
		{
			_startRequested = false;

			if (this.StopRequired != null)
			{
				this.StopRequired(this, new StopRequiredEventArgs(waitEngineReady));
			}
		}

		#endregion

        #region Private Methods

        private void GetAllMarketsHolidays(IEnumerable<ExchangeMarket> markets)
        {
            List<IEnumerable<DateTime>> listOfMarketHolidays = markets.Select(market => market.Holidays).Cast<IEnumerable<DateTime>>().ToList();
        	_allMarketsHolidays = listOfMarketHolidays.Aggregate(Enumerable.Intersect).ToList();
        }

        #endregion
    }
}
